﻿#region copyright
//                Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
//           (See accompanying file LICENSE.txt or 
//     copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Diagnostics.Contracts;
using System.Collections.Generic;

namespace CSFP
{
    /// <summary>
    /// This is a wrapper class for T[] which obeys the ISeq[T,S] contract, AS LONG AS the backing array is not modified.
    /// </summary>
    public struct ArraySeq<T> : ISeq<T, ArraySeq<T>>, IEnumerable<T>
    {
        readonly T[] _array;
        readonly int _index;

        public static readonly ArraySeq<T> Empty = new ArraySeq<T>(new T[0], 0);

        public ArraySeq(T[] array, int index)
        {
            Contract.Requires<ArgumentNullException>(array != null);

            _array = array;
            _index = index;
        }

        public bool IsValid { get { return _index < _array.Length; } }
        public T First { get { return _array[_index]; } }
        public ArraySeq<T> Next { get { return new ArraySeq<T>(_array, _index + 1); } }

        public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)_array).GetEnumerator(); } // Array will implement IEnumerable<T> only during runtime, so we have to cast
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return _array.GetEnumerator(); }
    }

    /// <summary>
    /// This is a wrapper class for IEnumerator[T] which DOES NOT obey the ISeq[T,S] contract, as it shares 
    /// the IEnumerator[T] instance among EnumeratorSeq instances and so destroys each other's position.
    /// However if we only do a single pass trough the ISeq[T,S] then we should not notice this behaviour.
    /// </summary>
    public struct EnumeratorSeq<T> : ISeq<T, EnumeratorSeq<T>>, IEnumerable<T>
    {
        readonly IEnumerator<T> _seq;
        readonly bool _valid;

        [CoverageExclude]
        private class NullEnumerator : IEnumerator<T>
        {
            public bool MoveNext() { return false; }
            public T Current { get { throw new NotImplementedException(); } }
            object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } }
            public void Reset() { throw new NotImplementedException(); }
            public void Dispose() { }
        }

        public static readonly EnumeratorSeq<T> Empty = new EnumeratorSeq<T>(new NullEnumerator(), false);

        public EnumeratorSeq(IEnumerator<T> seq, bool valid)
        {
            Contract.Requires<ArgumentNullException>(seq != null);

            _seq = seq;
            _valid = valid;
        }

        public bool IsValid { get { return _valid; } }
        public T First { get { return _seq.Current; } }
        public EnumeratorSeq<T> Next { get { return new EnumeratorSeq<T>(_seq, _seq.MoveNext()); } }

        // These two break the enumeration semantics as well, but at least "rewind" one position we have moved in Seq()...
        public IEnumerator<T> GetEnumerator() { return new SeqEnumerator<T, EnumeratorSeq<T>>(this); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }

    /// <summary>
    /// This is a IEnumerator[T] implementation wrapper class which is returned from almost all ISeq[T,S] implementations.
    /// </summary>
    public struct SeqEnumerator<T, S> : IEnumerator<T>
        where S : struct, ISeq<T, S>
    {
        S _seq;
        bool _beforeFirst;

        public SeqEnumerator(S seq)
        {
            _seq = seq;
            _beforeFirst = true;
        }

        public bool MoveNext()
        {
            if (_beforeFirst) {
                _beforeFirst = false;
                return _seq.IsValid;
            }
            _seq = _seq.Next;
            return _seq.IsValid;
        }

        public T Current { get { return _seq.First; } }
        object System.Collections.IEnumerator.Current { get { return Current; } }
        public void Reset() { throw new NotImplementedException(); }
        public void Dispose() { }
    }

    public static partial class Functions
    {
        /// <summary>
        /// Wraps an T[] array in an ISeq[T,S]. A null array results in an empty seq.
        /// </summary>
        public static ArraySeq<T> Seq<T>(this T[] array)
        {
            return array != null ? new ArraySeq<T>(array, 0) : ArraySeq<T>.Empty;
        }

        /// <summary>
        /// Wraps an IEnumerator[T] in an ISeq[T,S]. A null reference results in an empty seq.
        /// </summary>
        public static EnumeratorSeq<T> Seq<T>(this IEnumerable<T> seq)
        {
            if (seq == null)
                return EnumeratorSeq<T>.Empty;
            var en = seq.GetEnumerator();
            if (en == null)
                return EnumeratorSeq<T>.Empty;
            return new EnumeratorSeq<T>(en, en.MoveNext());
        }
    }
}
